<?php

namespace Xp\Myplugin;

use App\Other\Constants\Enum\UploadEnum;
use Hyperf\Collection\Arr;
use Hyperf\Contract\ContainerInterface;
use Throwable;
use function Hyperf\Config\config;

class UploadFileWithS3
{

    protected array $config;

    protected int $timeout = 3600;

    protected ContainerInterface $container;
    /** @var string */
    protected string $bucket;
    protected string $expire_hour;

    /**
     * @param ContainerInterface $container
     */
    public function __construct(ContainerInterface $container)
    {
        $config = config('aws.s3');
        $this->config = $config;
        $this->container = $container;
        $this->bucket = $config['bucket_name'];
        $this->expire_hour = $config['expire_hour'];
    }

    /**
     * 获取资源完整路
     * @param string $key
     * @return string
     */
    public function getImageUrl(string $key): string
    {
        return empty($key) ? '' : $this->config['vitual_host'] . '/' . $key;
    }

    /**
     * @param array $params
     * @param int $uid
     * @return array
     */
    public function formUpload(array $params,int $uid): array
    {
        //参考文档：https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
        $mimeType = Arr::get($params, 'mime_type', '');
        $fileName = Arr::get($params, 'file_name', '');
        $fileSize = (int)Arr::get($params, 'file_size', 0);

        [$extension, $mimeType] = $this->validateUploadType($mimeType, $fileName, $fileSize);

        [$dir, $fileName, $key] = $this->createDirAndKey($uid);

        $key = $key . '.' . $extension;
        //设置该policy超时时间是10s. 即这个policy过了这个有效时间，将不能访问。
        $end = time() + 60;
        $expiration = str_replace('+00:00', '.000Z', gmdate('c', $end));

        $conditions = [
            ['bucket' => $this->bucket],
            ['starts-with', '$key', $dir],
            ['content-length-range', 1, $fileSize],
            ['starts-with', '$Content-Type', $mimeType],
        ];

        $policyArr = ['expiration' => $expiration, 'conditions' => $conditions];
        $policy = base64_encode(json_encode($policyArr));

        //生成签名
        $signature = base64_encode(hash_hmac('sha1', $policy, $this->config['credentials']['secret'], true));

        // [$aes, $ivSuffix] = aesEncrypt($this->config['credentials']['key']);

        $response = [];
        // $response['ak'] = $aes;
        $response['ak'] = $this->config['credentials']['key'];
        $response['host'] = 'https://' . $this->bucket . '.' . $this->config['endpoint'];
        $response['policy'] = $policy;
        $response['signature'] = $signature;
        $response['key'] = $key;
        $response['mime_type'] = $mimeType;
        $response['url'] = $this->getImageUrl($key);
//        $response['str'] = $ivSuffix;
        return $response;
    }


    /**
     * 获取文件的dir和key
     * @param int $uid
     * @return array
     */
    protected function createDirAndKey(int $uid): array
    {
        // todo 存放路径是否需要改动
        $dir = "aiyou/{$uid}/" . date('Ymd') . '/';

        $characters = '1234567890abcdefghijklmnopqrstuvwxyz';
        $randomString = substr(str_shuffle($characters), 0, 5);

        $fileName = md5(microtime() . mt_rand(1000, 9999) . $randomString);

        $key = $dir . $fileName;

        return [$dir, $fileName, $key];
    }

    /**
     * 验证上传文件的格式以及大小，并返回文件后缀和mimeType
     * @param string $mimeType
     * @param string $fileName
     * @param int $fileSize
     * @param bool $isChickFileSize
     * @return array
     */
    protected function validateUploadType(string $mimeType = '', string $fileName = '', int $fileSize = 0, bool $isChickFileSize = false): array
    {
        $mimeType = strtolower($mimeType);
        $fileName = strtolower($fileName);
        try {
            if (!$mimeType && !$fileName) {
                throw new BusinessException(__('service.parameter_error'));
            }

            $extension = pathinfo($fileName, PATHINFO_EXTENSION);

            $allowFiles = $this->getAllowFiles();

            if (empty($extension)) {
                if (!empty($mimeType)) {
                    $extension = $this->getExtensionByMineType($mimeType, $allowFiles);
                } else {
                    throw new BusinessException(__('service.parameter_error'));
                }
            }


            if (empty($mimeType)) {
                if (!empty($extension)) {
                    $mimeType = $this->getMineTypeByExtension($extension, $allowFiles);
                } else {
                    throw new BusinessException(__('service.parameter_error'));
                }
            }

            if (!$this->checkFileInfo($extension, $mimeType)) {
                throw new BusinessException(__('service.parameter_error'));
            }

            if ($isChickFileSize && $fileSize > (int)$this->config['max_file_size']) {
                throw new BusinessException(__('upload.upload_size_verification_fail'));
            }
            return [$extension, $mimeType];
        } catch (BusinessException $e) {
            throw $e;
        } catch (Throwable $e) {
            throw new BusinessException(__('upload.upload_type_verification_fail'));
        }
    }


    /**
     * 检查mine_type是否和后缀能对应
     * @param string $fileExtension
     * @param string $fileMimeType
     * @param array $allowFiles
     * @return bool
     */
    protected function checkFileInfo(string $fileExtension, string $fileMimeType, array $allowFiles = []): bool
    {
        if (empty($allowFiles)) {
            $allowFiles = $this->getAllowFiles();
        }
        $ExRes = false;
        $MtRes = false;
        foreach ($allowFiles as $extension => $mineType) {
            if ($fileExtension === $extension) {
                $ExRes = true;
            }

            if ($fileMimeType === $mineType) {
                $MtRes = true;
            }

            if ($ExRes && $MtRes) {
                break;
            }
        }
        return $ExRes && $MtRes;
    }

    protected function getAllowFiles(): array
    {
        return array_merge(UploadEnum::IMAGES, UploadEnum::VIDEOS, UploadEnum::ADDITIONAL);

    }

    /**
     * 根据mineType获取文件后缀
     * @param string $fileMimeType
     * @param array $allowFiles
     * @return string
     */
    protected function getExtensionByMineType(string $fileMimeType, array $allowFiles = []): string
    {

        if (empty($allowFiles)) {
            $allowFiles = $this->getAllowFiles();
        }
        $fileExtension = '';
        foreach ($allowFiles as $extension => $mineType) {
            if ($fileMimeType === $mineType) {
                $fileExtension = $extension;
                break;
            }
        }

        return $fileExtension;
    }

    /**
     * 根据后缀获取mine_type
     * @param string $fileExtension
     * @param array $allowFiles
     * @return mixed
     */
    protected function getMineTypeByExtension(string $fileExtension, array $allowFiles = []): mixed
    {
        if (empty($allowFiles)) {
            $allowFiles = $this->getAllowFiles();
        }
        $fileMimeType = '';
        foreach ($allowFiles as $extension => $mineType) {
            if ($fileExtension === $extension) {
                $fileMimeType = $mineType;
                break;
            }
        }

        return $fileMimeType;
    }

}